package com.lagou.aspect;

import com.alibaba.fastjson.JSON;
import com.lagou.common.ResponseCode;
import com.lagou.common.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Aspect
@Service
@ControllerAdvice
@Slf4j
public class ControllerAspect {

    @Autowired
    private HttpServletResponse response;

    @Value("${spring.application.name}")
    private String serviceName;

    @Pointcut("execution(* com..*.controller.*Controller.*(..))")
    public void controllerMethod(){}

    @Around("controllerMethod()")
    public Object process(ProceedingJoinPoint pjp) throws Throwable {

        Object result;
        try {
            result = pjp.proceed();
        } catch (Exception e) {
            log.error("系统异常：" + e.getMessage(), e);
            result = ResponseResult.exception(ResponseCode.SYSTEM_EXCEPTION, e.getMessage());
        }
        if (result instanceof ModelAndView) {
            result = result;
        } else { //default result type is json
            result = processJsonResult(pjp, result);
        }
        return result;
    }

    private Object processJsonResult(ProceedingJoinPoint pjp,Object obj) throws Throwable {
        Object result;
        response.setHeader("gateway-token", serviceName + "/json");
        if (obj instanceof ResponseResult) {
            result = obj;
        } else {
            result = ResponseResult.success(obj);
        }
        writeLog(pjp, result);
        return result;
    }

    @ExceptionHandler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public Object handleException(MethodArgumentNotValidException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        String msg = fieldError.getField() + "-" + fieldError.getDefaultMessage();
        ResponseResult responseResult = ResponseResult.exception(ResponseCode.PARAM_EXCEPTION,msg);
        return new ResponseEntity<>(responseResult, HttpStatus.OK);
    }

    @ExceptionHandler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public Object handleException(MissingServletRequestParameterException e) {
        ResponseResult responseResult = ResponseResult.exception(ResponseCode.PARAM_EXCEPTION,e.getMessage());
        return new ResponseEntity<>(responseResult, HttpStatus.OK);
    }


    private void writeLog(ProceedingJoinPoint pjp, Object result){
        try {
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            String method = signature.getDeclaringTypeName() + "." + signature.getName();
            Object param = null;
            Object[] args = pjp.getArgs();
            if (args != null && args.length > 0){
                if (args.length == 1){
                    param = JSON.toJSONString(args[0]);
                }else {
                    String[] argNames = signature.getParameterNames();
                    StringBuilder buf = new StringBuilder();
                    for (int i = 0; i < args.length; i++){
                        if (args[i] instanceof ServletRequest) {
                            continue;
                        }else if (args[i] instanceof BindingResult) {
                            continue;
                        }else if (args[i] instanceof MultipartFile) {
                            continue;
                        } else if(args[i] instanceof ServletResponse){
                            continue;
                        }

                        buf.append(argNames[i] + "=" + JSON.toJSONString(args[i]) + " ");
                    }
                    param = buf.toString();
                }
            }
            if (result instanceof ResponseResult) {
                Object data = ((ResponseResult) result).getData();
                if (data instanceof List) {
                    if (((List) data).size() > 20) {
                        log.debug("our method: method:{}, param:{}, result size:{}", method, param, ((List) data).size());
                    }
                }else {
                    log.debug("our method: method:{}, param:{}, result:{}", method, param, JSON.toJSONString(result));
                }
            }
        } catch (Exception e) {
            log.info("打印日志出错",e);
        }
    }

}